﻿/*************************************************************************************
 * 工    具：  HK_TcpCommunicateSystem
 * 
 * 描    述：  本系统层是一个Unity的网络通信系统（不支持WebGL），包含了Server端和Client端两个部分，
 *            使用 TCP 协议实现客户端和服务器之间的消息传递。
 *            该系统通过 QFramework 框架进行管理，提供了服务器端和客户端的启动、关闭及消息发送等功能。
 * 
 * 版    本：  V1.0
 * 作    者：  京产肠饭
 * 
 * 创    建：  2024/07/04  V1.0
 *            
 * 链    接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using QFramework;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace HKTools
{
    public interface IHK_TcpClientSystem : ISystem
    {
        bool IsClientRunning { get; set; }

        /// <summary>
        /// Client端 开启
        /// </summary>
        /// <param name="serverIP">Server端IP</param>
        /// <param name="serverPort">Server端端口号</param>
        void StartClient(string serverIP, int serverPort);

        /// <summary>
        /// Client端 关闭
        /// </summary>
        void CloseClient();

        /// <summary>
        /// Client端 给 Server端，发送消息
        /// </summary>
        /// <param name="msg"></param>
        void SendMsg_ClientToServer(string msg);

        /// <summary>
        /// 设置是否开启 系统层Debug
        /// </summary>
        /// <param name="isOn"></param>
        void SetDebugState(bool isOn);
    }

    public class HK_TcpClientSystem : AbstractSystem, IHK_TcpClientSystem
    {
        public bool IsClientRunning { get; set; }

        Socket clientSocket;
        string serverIP;
        int serverPort;

        SynchronizationContext mainThreadContext;

        bool isConnecting = false;

        bool isDebugOn = false;

        protected override void OnInit()
        {
            IsClientRunning = false;
            isConnecting = false;
            mainThreadContext = SynchronizationContext.Current;

            // Client端 系统层不进行Debug打印
            SetDebugState(false);
        }

        public void StartClient(string serverIP, int serverPort)
        {
            if (IsClientRunning)
            {
                HKDebug.LogWarning("Client端已启动，不能重复启动！");
                return;
            }

            if (isConnecting)
            {
                HKDebug.LogWarning("Client端启动中，不能重复启动！");
                return;
            }

            isConnecting = true;

            this.serverIP = serverIP;
            this.serverPort = serverPort; ;

            ThreadPool.QueueUserWorkItem(ClientConnect_ThreadMethod);
        }

        void ClientConnect_ThreadMethod(object obj)
        {
            clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(serverIP), serverPort);

            HKDebug.Log($"尝试连接 Server端...(0/5)");

            try
            {
                int attempCount = 0;
                int maxAttempts = 5; // 最大尝试次数
                int delayAttempts = 200; // 每次尝试之间的延迟（毫秒）

                while (!IsClientRunning && attempCount < maxAttempts)
                {
                    try
                    {
                        clientSocket.Connect(endPoint);

                        HKDebug.Log("Client端 开启", LogColor.Blue);

                        IsClientRunning = true;
                        isConnecting = false;

                        mainThreadContext.Post(_ =>
                        {
                            this.SendEvent(new Event_TCP_ConnectToServer() { });
                        }, null);

                        ThreadPool.QueueUserWorkItem(ReceiveMsg_ThreadMethod);

                        break;
                    }
                    catch
                    {
                        attempCount++;
                        HKDebug.Log($"尝试连接 Server端...({attempCount}/{maxAttempts})");

                        if (attempCount >= maxAttempts)
                        {
                            //Debug.LogWarning("无法连接到 Server端，已达到最大尝试次数!");
                            isConnecting = false;

                            mainThreadContext.Post(_ =>
                            {
                                this.SendEvent(new Event_TCP_ConnectToServerOvertime() { });
                            }, null);

                            return;
                        }

                        Thread.Sleep(delayAttempts);
                    }
                }
            }
            catch (SocketException e)
            {
                HKDebug.LogError(e.Message);
            }
        }

        void ReceiveMsg_ThreadMethod(object obj)
        {
            while (IsClientRunning)
            {
                if (clientSocket.Available <= 0)
                {
                    Thread.Sleep(10); // 避免忙等待
                    continue;
                }

                byte[] msg = new byte[1024];
                int msgLength = clientSocket.Receive(msg);
                string msgStr = Encoding.UTF8.GetString(msg, 0, msgLength);

                mainThreadContext.Post(_ =>
                {
                    if (isDebugOn)
                        HKDebug.Log("Server端 消息：" + msgStr);

                    if (msgStr.Trim().Equals("SERVERCLOSE"))
                    {
                        CloseClient();
                    }
                    else
                    {
                        this.SendEvent(new Event_TCP_FromServerMsg() { _msg = msgStr });
                    }
                }, null);
            }
        }

        public void SendMsg_ClientToServer(string msg)
        {
            if (!IsClientRunning)
                return;

            // 添加检查以确保Socket已连接
            if (clientSocket != null && clientSocket.Connected)
                clientSocket.Send(Encoding.UTF8.GetBytes(msg));
        }

        public void CloseClient()
        {
            if (isConnecting)
            {
                if (clientSocket != null)
                {
                    if (clientSocket.Connected)
                        clientSocket.Shutdown(SocketShutdown.Both);

                    clientSocket.Close();
                    clientSocket = null;
                }

                isConnecting = false;
                IsClientRunning = false;
                return;
            }

            if (!IsClientRunning)
                return;

            SendMsg_ClientToServer("CLIENTCLOSE");

            if (clientSocket != null)
            {
                if (clientSocket.Connected)
                    clientSocket.Shutdown(SocketShutdown.Both);

                clientSocket.Close();
                clientSocket = null;
            }

            isConnecting = false;
            IsClientRunning = false;

            mainThreadContext.Post(_ =>
            {
                this.SendEvent(new Event_TCP_DisconnectToServer() { });
                HKDebug.Log("Client端 关闭", LogColor.Red);
            }, null);
        }

        public void SetDebugState(bool isOn)
        {
            isDebugOn = isOn;
        }
    }
}